package org.ipms.c.common.persistence.mybaits;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.session.SqlSession;
import org.ipms.c.common.exception.DbException;
import org.ipms.c.common.persistence.mybaits.annotion.MapperCondition;
import org.ipms.c.common.persistence.web.SpringContext;
import org.ipms.c.common.utils.UUIDGenerator;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.entity.TableInfo;
import com.baomidou.mybatisplus.enums.SqlMethod;
import com.baomidou.mybatisplus.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.mapper.Condition;
import com.baomidou.mybatisplus.mapper.SqlHelper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.toolkit.MapUtils;
import com.baomidou.mybatisplus.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.toolkit.StringUtils;
import com.baomidou.mybatisplus.toolkit.TableInfoHelper;

@SuppressWarnings({ "unchecked", "rawtypes" })
public abstract class BaseService<T extends BaseEntity, QO extends BaseQO> {

	protected BaseSimpleMapper<T, QO> baseSimpleMapper() {
		MapperCondition mc = getEntityClass().getAnnotation(MapperCondition.class);
		return (BaseSimpleMapper<T, QO>) SpringContext.getBean(mc.mapperBean());
	}

	abstract protected Class<T> getEntityClass();

	/**
	 * 参照xml查询单个对象
	 * 
	 * @param qo
	 * @return
	 */
	@Transactional(readOnly = true)
	public T queryUnique(QO qo) {
		List<T> list = queryList(qo);
		if(list == null)return null;
		return list.get(0);
	}

	@Transactional(readOnly = true)
	public List<T> queryList(QO qo) {
		return baseSimpleMapper().queryList(qo);
	}
	
	
	
	
	/**
	 * 判断数据库操作是否成功 注意！！ 该方法为 Integer 判断，不可传入 int 基本类型
	 * 
	 * @param result
	 *            数据库操作返回影响条数
	 * @return boolean
	 */
	protected static boolean retBool(Integer result) {
		return SqlHelper.retBool(result);
	}

	protected Class<T> currentModelClass() {
		return ReflectionKit.getSuperClassGenricType(getClass(), 1);
	}

	/**
	 * 批量操作 SqlSession
	 */
	protected SqlSession sqlSessionBatch() {
		return SqlHelper.sqlSessionBatch(currentModelClass());
	}

	/**
	 * 获取SqlStatement
	 */
	protected String sqlStatement(SqlMethod sqlMethod) {
		return SqlHelper.table(currentModelClass()).getSqlStatement(sqlMethod.getMethod());
	}

	@Transactional
	public T insert(T entity) throws DbException {
		if (entity == null)
			return null;
		entity.setSid(UUIDGenerator.getUUID());
		if (!retBool(baseSimpleMapper().insert(entity))) {
			throw new DbException(DbException.DATA_ADD, "数据插入失败！");
		}
		return entity;
	}

	@Transactional
	public boolean insertAllColumn(T entity) {
		return retBool(baseSimpleMapper().insertAllColumn(entity));
	}

	@Transactional
	public boolean insertBatch(List<T> entityList) {
		return insertBatch(entityList, 30);
	}

	/**
	 * 批量插入
	 */
	@Transactional
	public boolean insertBatch(List<T> entityList, int batchSize) {
		if (CollectionUtils.isEmpty(entityList)) {
			throw new IllegalArgumentException("Error: entityList must not be empty");
		}
		try (SqlSession batchSqlSession = sqlSessionBatch()) {
			int size = entityList.size();
			String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE);
			for (int i = 0; i < size; i++) {
				batchSqlSession.insert(sqlStatement, entityList.get(i));
				if (i >= 1 && i % batchSize == 0) {
					batchSqlSession.flushStatements();
				}
			}
			batchSqlSession.flushStatements();
		} catch (Throwable e) {
			throw new MybatisPlusException("Error: Cannot execute insertBatch Method. Cause", e);
		}
		return true;
	}

	/**
	 * TableId 注解存在更新记录，否插入一条记录
	 * 
	 * @param entity
	 *            实体对象
	 * @return boolean
	 * @throws DbException
	 */
	@Transactional
	public T insertOrUpdate(T entity) throws DbException {
		if (null != entity) {
			Class<?> cls = entity.getClass();
			TableInfo tableInfo = TableInfoHelper.getTableInfo(cls);
			if (null != tableInfo && StringUtils.isNotEmpty(tableInfo.getKeyProperty())) {
				Object idVal = ReflectionKit.getMethodValue(cls, entity, tableInfo.getKeyProperty());
				if (StringUtils.checkValNull(idVal)) {
					return insert(entity);
				} else {
					/*
					 * 更新成功直接返回，失败执行插入逻辑
					 */
					if (!updateById(entity)) {
						insert(entity);
					}
					return entity;
				}
			} else {
				throw new MybatisPlusException("Error:  Can not execute. Could not find @TableId.");
			}
		}
		return null;
	}

	@Transactional
	public boolean insertOrUpdateAllColumn(T entity) {
		if (null != entity) {
			Class<?> cls = entity.getClass();
			TableInfo tableInfo = TableInfoHelper.getTableInfo(cls);
			if (null != tableInfo && StringUtils.isNotEmpty(tableInfo.getKeyProperty())) {
				Object idVal = ReflectionKit.getMethodValue(cls, entity, tableInfo.getKeyProperty());
				if (StringUtils.checkValNull(idVal)) {
					return insertAllColumn(entity);
				} else {
					/*
					 * 更新成功直接返回，失败执行插入逻辑
					 */
					return updateAllColumnById(entity) || insertAllColumn(entity);
				}
			} else {
				throw new MybatisPlusException("Error:  Can not execute. Could not find @TableId.");
			}
		}
		return false;
	}

	@Transactional
	public boolean insertOrUpdateBatch(List<T> entityList) {
		return insertOrUpdateBatch(entityList, 30);
	}

	@Transactional
	public boolean insertOrUpdateBatch(List<T> entityList, int batchSize) {
		return insertOrUpdateBatch(entityList, batchSize, true);
	}

	@Transactional
	public boolean insertOrUpdateAllColumnBatch(List<T> entityList) {
		return insertOrUpdateBatch(entityList, 30, false);
	}

	@Transactional
	public boolean insertOrUpdateAllColumnBatch(List<T> entityList, int batchSize) {
		return insertOrUpdateBatch(entityList, batchSize, false);
	}

	/**
	 * 批量插入修改
	 * 
	 * @param entityList
	 *            实体对象列表
	 * @param batchSize
	 *            批量刷新个数
	 * @param selective
	 *            是否滤掉空字段
	 * @return boolean
	 */
	private boolean insertOrUpdateBatch(List<T> entityList, int batchSize, boolean selective) {
		if (CollectionUtils.isEmpty(entityList)) {
			throw new IllegalArgumentException("Error: entityList must not be empty");
		}
		try (SqlSession batchSqlSession = sqlSessionBatch()) {
			int size = entityList.size();
			for (int i = 0; i < size; i++) {
				if (selective) {
					insertOrUpdate(entityList.get(i));
				} else {
					insertOrUpdateAllColumn(entityList.get(i));
				}
				if (i >= 1 && i % batchSize == 0) {
					batchSqlSession.flushStatements();
				}
			}
			batchSqlSession.flushStatements();
		} catch (Throwable e) {
			throw new MybatisPlusException("Error: Cannot execute insertOrUpdateBatch Method. Cause", e);
		}
		return true;
	}

	@Transactional
	public boolean deleteById(Serializable id) {
		return retBool(baseSimpleMapper().deleteById(id));
	}

	@Transactional
	public boolean deleteByMap(Map<String, Object> columnMap) {
		if (MapUtils.isEmpty(columnMap)) {
			throw new MybatisPlusException("deleteByMap columnMap is empty.");
		}
		return retBool(baseSimpleMapper().deleteByMap(columnMap));
	}

	@Transactional
	public boolean delete(Wrapper<T> wrapper) {
		return retBool(baseSimpleMapper().delete(wrapper));
	}

	@Transactional
	public boolean deleteBatchIds(List<? extends Serializable> idList) {
		return retBool(baseSimpleMapper().deleteBatchIds(idList));
	}

	@Transactional
	public boolean updateById(T entity) {
		return retBool(baseSimpleMapper().updateById(entity));
	}

	@Transactional
	public boolean updateAllColumnById(T entity) {
		return retBool(baseSimpleMapper().updateAllColumnById(entity));
	}

	@Transactional
	public boolean update(T entity, Wrapper<T> wrapper) {
		return retBool(baseSimpleMapper().update(entity, wrapper));
	}

	@Transactional
	public boolean updateBatchById(List<T> entityList) {
		return updateBatchById(entityList, 30);
	}

	@Transactional
	public boolean updateBatchById(List<T> entityList, int batchSize) {
		return updateBatchById(entityList, batchSize, true);
	}

	@Transactional
	public boolean updateAllColumnBatchById(List<T> entityList) {
		return updateAllColumnBatchById(entityList, 30);
	}

	@Transactional
	public boolean updateAllColumnBatchById(List<T> entityList, int batchSize) {
		return updateBatchById(entityList, batchSize, false);
	}

	/**
	 * 根据主键ID进行批量修改
	 */
	private boolean updateBatchById(List<T> entityList, int batchSize, boolean selective) {
		if (CollectionUtils.isEmpty(entityList)) {
			throw new IllegalArgumentException("Error: entityList must not be empty");
		}
		try (SqlSession batchSqlSession = sqlSessionBatch()) {
			int size = entityList.size();
			SqlMethod sqlMethod = selective ? SqlMethod.UPDATE_BY_ID : SqlMethod.UPDATE_ALL_COLUMN_BY_ID;
			String sqlStatement = sqlStatement(sqlMethod);
			for (int i = 0; i < size; i++) {
				MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
				param.put("et", entityList.get(i));
				batchSqlSession.update(sqlStatement, param);
				if (i >= 1 && i % batchSize == 0) {
					batchSqlSession.flushStatements();
				}
			}
			batchSqlSession.flushStatements();
		} catch (Throwable e) {
			throw new MybatisPlusException("Error: Cannot execute updateBatchById Method. Cause", e);
		}
		return true;
	}

	public T selectById(Serializable id) {
		return baseSimpleMapper().selectById(id);
	}

	public List<T> selectBatchIds(List<? extends Serializable> idList) {
		return baseSimpleMapper().selectBatchIds(idList);
	}

	public List<T> selectByMap(Map<String, Object> columnMap) {
		return baseSimpleMapper().selectByMap(columnMap);
	}

	public T selectOne(Wrapper<T> wrapper) {
		return SqlHelper.getObject(baseSimpleMapper().selectList(wrapper));
	}

	public Map<String, Object> selectMap(Wrapper<T> wrapper) {
		return SqlHelper.getObject(baseSimpleMapper().selectMaps(wrapper));
	}

	public Object selectObj(Wrapper<T> wrapper) {
		return SqlHelper.getObject(baseSimpleMapper().selectObjs(wrapper));
	}

	public int selectCount(Wrapper<T> wrapper) {
		return SqlHelper.retCount(baseSimpleMapper().selectCount(wrapper));
	}

	public List<T> selectList(Wrapper<T> wrapper) {
		return baseSimpleMapper().selectList(wrapper);
	}

	public Page<T> selectPage(Page<T> page) {
		return selectPage(page, Condition.EMPTY);
	}

	public List<Map<String, Object>> selectMaps(Wrapper<T> wrapper) {
		return baseSimpleMapper().selectMaps(wrapper);
	}

	public List<Object> selectObjs(Wrapper<T> wrapper) {
		return baseSimpleMapper().selectObjs(wrapper);
	}

	public Page<Map<String, Object>> selectMapsPage(Page page, Wrapper<T> wrapper) {
		SqlHelper.fillWrapper(page, wrapper);
		page.setRecords(baseSimpleMapper().selectMapsPage(page, wrapper));
		return page;
	}

	public Page<T> selectPage(Page<T> page, Wrapper<T> wrapper) {
		SqlHelper.fillWrapper(page, wrapper);
		page.setRecords(baseSimpleMapper().selectPage(page, wrapper));
		return page;
	}
}
